/*
 * Copyright (c) 2024 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "common/abc_code_converter.h"
#include "proto_data_accessor-inl.h"
#include "method_data_accessor-inl.h"

namespace panda::abc2program {

pandasm::Ins *AbcCodeConverter::BytecodeInstructionToPandasmInstruction(BytecodeInstruction bc_ins,
                                                                       panda_file::File::EntityId method_id) const
{
    auto opcode = BytecodeOpcodeToPandasmOpcode(bc_ins.GetOpcode());
    std::vector<uint16_t> regs;
    std::vector<pandasm::IType> imms;
    std::vector<std::string> ids;

    const BytecodeInstruction::Format format = bc_ins.GetFormat();

    switch (bc_ins.GetOpcode()) {
% Panda::instructions.each do |i|
%   imm_count = 0
%   reg_count = 0
%   id_count = 0
        case BytecodeInstruction::Opcode::<%= i.opcode.upcase %>:
%
%   i.operands.each do |operand|
%     if operand.imm?
%       if i.jump?
            ids.push_back(std::to_string(static_cast<int32_t>(bc_ins.GetImm<BytecodeInstruction::Format::<%=i.format.pretty.upcase%>, <%=imm_count%>, true>())));
%       elsif operand.is_float_imm?
            imms.push_back(bit_cast<double>(bc_ins.GetImm<BytecodeInstruction::Format::<%=i.format.pretty.upcase%>, <%=imm_count%>, true>()));
%       elsif operand.is_signed_imm?
            imms.push_back(static_cast<int64_t>(bc_ins.GetImm<BytecodeInstruction::Format::<%=i.format.pretty.upcase%>, <%=imm_count%>, true>()));
%       elsif operand.is_unsigned_imm?
            imms.push_back(static_cast<int64_t>(bc_ins.GetImm<BytecodeInstruction::Format::<%=i.format.pretty.upcase%>, <%=imm_count%>, false>()));
%       else
%            raise "Incorrect imm type #{operand.type}"
%       end
%         imm_count += 1
%
%     elsif operand.reg?
            regs.push_back(bc_ins.GetVReg(<%=reg_count%>));
%         reg_count += 1
%     elsif operand.id?
            ids.push_back(IDToString(bc_ins, method_id, <%=id_count%>));
%         id_count += 1
%     end   
%   end
            break;
% end
        default:
            break;
    }

    if ((pandasm::INST_FLAGS_TABLE[static_cast<size_t>(opcode)] & pandasm::InstFlags::CALL) != 0) {
        // clearing excessive arguments if there are any
        // if format has ID in it - receive it. else instruction is indirect call and id = method_id
        panda_file::File::EntityId id;

        if (bc_ins.HasId(format, 0)) {
            auto idx = bc_ins.GetId().AsIndex();
            id = file_.ResolveMethodIndex(method_id, idx);
        } else {
            id = method_id;
        }
        
        panda_file::MethodDataAccessor mda(file_, id);
        panda_file::ProtoDataAccessor pda(file_, mda.GetProtoId());

        int overhead;

        if (mda.IsStatic()) {
            overhead = regs.size() - pda.GetNumArgs();
        } else {
            overhead = regs.size() - pda.GetNumArgs() - 1;
        }
        if (overhead < 0 || overhead > static_cast<int>(regs.size())) {
            auto *ins = pandasm::Ins::CreateIns(opcode, regs, imms, ids);
            LOG(ERROR, ABC2PROGRAM)
                << "> error encountered in code of " << std::dec << method_id.GetOffset() << " ("
                << "0x" << std::hex << method_id.GetOffset() << ") while disassembling call <" << ins->ToString("")
                << ">. Invalid method id given or corrupted: calculated overhead (difference "
                   "between amount of registers specified by instruction format and amount of function's arguments) "
                   "exceeds number of registers specified in ISA or is lesser than 0!";
            delete ins;
            return nullptr;
        }

        for (int i = 0; i < overhead; i++) {
            regs.pop_back();
        }
    }

    return pandasm::Ins::CreateIns(opcode, regs, imms, ids);
}

}  // namespace panda::abc2program
